package com.jvmoutofmemory.platform.pushutil.dao;

import lombok.extern.slf4j.Slf4j;
import com.jvmoutofmemory.platform.pushutil.dto.KV;
import com.jvmoutofmemory.platform.pushutil.dto.SqlSegment;
import com.jvmoutofmemory.platform.pushutil.util.OgnlUtil;
import com.jvmoutofmemory.platform.pushutil.util.PushStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;

import javax.persistence.Column;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.sql.DataSource;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

/**
 * @author adam
 * @date 2021-11-17
 */
@Slf4j
public class BaseDao {

    protected final JdbcTemplate jdbcTemplate;

    public BaseDao(DataSource dataSource) {
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }

    /**
     * 新增
     * @param entity
     * @return
     */
    public int insert(Object entity) {
        return this.insert(entity, true);
    }

    /**
     * 新增
     * @param entity
     * @return
     */
    public int insertNonNull(Object entity) {
        return this.insert(entity, false);
    }

    /**
     * 更新
     * @param object
     * @return
     */
    public int updateById(Object object) {
        return updateById(object, true);
    }

    /**
     * 选择性更新， 不更新为null的属性
     * @param object
     * @return
     */
    public int updateByIdNonNull(Object object) {
        return updateById(object, false);
    }

    protected int updateById(Object object, boolean containsNull) {
        SqlSegment segment = getUpdateSegment(object, containsNull);
        Assert.notNull(segment, "Can not generate update sql !");
        return doUpdate(segment);
    }

    protected int insert(Object entity, boolean containsNull) {
        SqlSegment segment = getInsertSegment(entity, containsNull);
        Assert.notNull(segment, "Can not generate insert sql !");
        return doUpdate(segment);
    }

    protected int doUpdate(SqlSegment segment) {
        return jdbcTemplate.update(segment.getSql(), segment.getParamArray());
    }

    /**
     * 生成插入的SQL
     * @param entity
     * @param containsNull
     * @return
     */
    protected SqlSegment getInsertSegment(Object entity, boolean containsNull) {
        SqlSegment segment = new SqlSegment();
        StringBuilder base = new StringBuilder();
        base.append("insert into ").append(getTableName(entity.getClass())).append(" (");
        StringBuilder values = new StringBuilder();

        ReflectionUtils.doWithFields(entity.getClass(), new ReflectionUtils.FieldCallback() {
            @Override
            public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
                int modifiers = field.getModifiers();
                if (Modifier.isFinal(modifiers)
                        || Modifier.isStatic(modifiers)
                        || Modifier.isNative(modifiers)
                        || Modifier.isTransient(modifiers)) {
                    return;
                }

                Column column = AnnotationUtils.findAnnotation(field, Column.class);
                if (column == null) {
                    return;
                }

                String fieldName = field.getName();
                String columnName = StringUtils.isEmpty(column.name()) ? PushStringUtils.humpToUnderScore(fieldName) : column.name();
                Object value = OgnlUtil.getValue(entity, fieldName);
                if (value != null || containsNull) {
                    base.append(columnName).append(",");
                    values.append(" ?,");
                    segment.getParams().add(value);
                }
            }
        });

        base.deleteCharAt(base.length() - 1);
        values.deleteCharAt(values.length() - 1);
        segment.setSql(base.append(" ) values ( ").append(values).append(" )").toString());
        return segment;
    }

    /**
     * 生成更新的SQL
     * @param entity
     * @param containsNull
     * @return
     */
    protected SqlSegment getUpdateSegment(Object entity, boolean containsNull) {
        SqlSegment segment = new SqlSegment();
        StringBuilder base = new StringBuilder();
        base.append(" update ").append(getTableName(entity.getClass())).append(" set ");
        StringBuilder set = new StringBuilder();

        KV<String, Object> primaryKey = new KV<>();

        ReflectionUtils.doWithFields(entity.getClass(), new ReflectionUtils.FieldCallback() {
            @Override
            public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
                int modifiers = field.getModifiers();
                if (Modifier.isFinal(modifiers)
                        || Modifier.isStatic(modifiers)
                        || Modifier.isNative(modifiers)
                        || Modifier.isTransient(modifiers)) {
                    return;
                }

                Column column = AnnotationUtils.findAnnotation(field, Column.class);
                if (column == null) {
                    return;
                }

                String fieldName = field.getName();
                String columnName = StringUtils.isEmpty(column.name()) ? PushStringUtils.humpToUnderScore(fieldName) : column.name();

                Object value = OgnlUtil.getValue(entity, fieldName);
                if (value != null || containsNull) {
                    set.append(columnName).append(" = ?,");
                    segment.getParams().add(value);
                }

                if (StringUtils.isEmpty(primaryKey.getKey())) {
                    Id id = AnnotationUtils.findAnnotation(field, Id.class);
                    if (id != null) {
                        primaryKey.setKey(columnName);
                        primaryKey.setValue(value);
                    }
                }
            }
        });

        if (set.length() == 0) {
            log.error("No column need to be updated !");
            return null;
        }

        if (StringUtils.isEmpty(primaryKey.getKey()) || primaryKey.getValue() == null) {
            log.error("No primary key column found or no primary key value found !");
            return null;
        }

        set.deleteCharAt(set.length() - 1);
        base.append(set)
                .append(" where ").append(primaryKey.getKey()).append(" = ? ");
        segment.setSql(base.toString());
        segment.getParams().add(primaryKey.getValue());
        return segment;
    }

    /**
     * 获取表名
     * @param entity
     * @return
     */
    protected String getTableName(Class entity) {
        Table table = AnnotationUtils.findAnnotation(entity, Table.class);
        if (table != null && !StringUtils.isEmpty(table.name())) {
            return table.name();
        }

        String className = entity.getSimpleName();
        return PushStringUtils.humpToUnderScore(className);
    }

}
